package me.shenfan.updateapp;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.IBinder;
import android.os.StatFs;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;

import static me.shenfan.updateapp.UpdateUtil.webIntent;


public class UpdateService extends Service {
    public static final String TAG = "UpdateService";
    public static final boolean DEBUG = false;

    public static final String ACTION_UPDATE = "ACTION_UPDATE";
    public static final String EXTRA_UPDATE = "EXTRA_UPDATE";
    public static final String EXTRA_UPDATE_STATUS = "EXTRA_UPDATE_STATUS";
    public static final String EXTRA_UPDATE_PROGRESS = "EXTRA_UPDATE_PROGRESS";
    public static final String EXTRA_UPDATE_FILEPATH = "EXTRA_UPDATE_FILEPATH";
    public static final String EXTRA_UPDATE_RESULT_CODE = "EXTRA_UPDATE_RESULT_CODE";

    //下载状态
    public static final int UPDATE_STATUS_START = 0;
    public static final int UPDATE_STATUS_PROGRESS = 1;
    public static final int UPDATE_STATUS_SUCCESS = 2;
    public static final int UPDATE_STATUS_ERROR = 3;

    public static final int UPDATE_RESULT_SUCCESS = 1;//连接服务器错误
    public static final int UPDATE_RESULT_ERROR_NETWORK = 2;//网络错误
    public static final int UPDATE_RESULT_ERROR_SERVER = 3;//连接服务器错误
    public static final int UPDATE_RESULT_ERROR_SDCARD = 4;//内存卡容量不足
    public static final int UPDATE_RESULT_ERROR_OTHER = 5;//其他异常

    private UpdateHelper.Builder mUpdate;

    private boolean startDownload;//开始下载
    private int lastProgressNumber;
    private NotificationManager manager;
    private NotificationCompat.Builder builder;
    private int notifyId;
    private String appName;
    private LocalBroadcastManager localBroadcastManager;
    private DownloadApk downloadApkTask;

    private UpdateSharedPreferences mUpdateSharedPreferences;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        appName = UpdateUtil.getApplicationName(this);
        mUpdateSharedPreferences = new UpdateSharedPreferences(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!startDownload && intent != null) {
            startDownload = true;
            mUpdate = (UpdateHelper.Builder) intent.getSerializableExtra(EXTRA_UPDATE);

            if (mUpdate.icoResId == UpdateHelper.Builder.DEFAULT_RES_ID) {
                mUpdate.setIcoResId(UpdateUtil.getIcon(this));
            }
            if (mUpdate.icoSmallResId == UpdateHelper.Builder.DEFAULT_RES_ID) {
                mUpdate.setIcoSmallResId(UpdateUtil.getIcon(this));
            }

            notifyId = startId;
            buildNotification();
            localBroadcastManager = LocalBroadcastManager.getInstance(this);
            downloadApkTask = new DownloadApk(this);
            downloadApkTask.execute(mUpdate.url);
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (downloadApkTask != null) {
            downloadApkTask.cancel(true);
        }
        builder = null;
    }


    private void buildNotification() {
        manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        builder = new NotificationCompat.Builder(this);
        builder.setContentTitle(appName)
                .setWhen(System.currentTimeMillis())
                .setProgress(100, 1, false)
                .setSmallIcon(mUpdate.icoSmallResId)
                .setLargeIcon(BitmapFactory.decodeResource(
                        getResources(), mUpdate.icoResId))
                .setDefaults(mUpdate.downloadNotificationFlag);

        manager.notify(notifyId, builder.build());
    }

    private void start() {
        builder.setContentText(getString(R.string.update_app_model_prepare, appName));
        manager.notify(notifyId, builder.build());

        Intent localIntent = new Intent(ACTION_UPDATE);
        localIntent.putExtra(EXTRA_UPDATE_STATUS, UPDATE_STATUS_START);
        localBroadcastManager.sendBroadcast(localIntent);
    }

    /**
     * @param progress download percent , max 100
     */
    private void update(int progress) {
        if (progress - lastProgressNumber > mUpdate.updateProgress) {
            lastProgressNumber = progress;
            builder.setProgress(100, progress, false);
            builder.setContentText(getString(R.string.update_app_model_progress, progress, "%"));
            manager.notify(notifyId, builder.build());

            Intent localIntent = new Intent(ACTION_UPDATE);
            localIntent.putExtra(EXTRA_UPDATE_STATUS, UPDATE_STATUS_PROGRESS);
            localIntent.putExtra(EXTRA_UPDATE_PROGRESS, progress);
            localBroadcastManager.sendBroadcast(localIntent);
        }
    }

    private void success() {
        builder.setProgress(0, 0, false);
        builder.setContentText(getString(R.string.update_app_model_success));
        Intent i = UpdateUtil.installIntent(mUpdate.storeDir.getAbsolutePath());
        PendingIntent intent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(intent);
        builder.setDefaults(mUpdate.downloadSuccessNotificationFlag);
        builder.setContentIntent(intent);
        manager.notify(notifyId, builder.build());

        Intent localIntent = new Intent(ACTION_UPDATE);
        localIntent.putExtra(EXTRA_UPDATE_STATUS, UPDATE_STATUS_SUCCESS);
        localIntent.putExtra(EXTRA_UPDATE_FILEPATH, mUpdate.storeDir.getAbsolutePath());
        localBroadcastManager.sendBroadcast(localIntent);

        if (mUpdate.isAutoInstall) {
            startActivity(i);
        }
        stopSelf();
    }

    private void error(int resultCode) {
        Intent i = webIntent(mUpdate.url);
        PendingIntent intent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentText(getString(R.string.update_app_model_error));
        builder.setContentIntent(intent);
        builder.setProgress(0, 0, false);
        builder.setDefaults(mUpdate.downloadErrorNotificationFlag);
        builder.setContentIntent(intent);
        manager.notify(notifyId, builder.build());

        Intent localIntent = new Intent(ACTION_UPDATE);
        localIntent.putExtra(EXTRA_UPDATE_STATUS, UPDATE_STATUS_ERROR);
        localIntent.putExtra(EXTRA_UPDATE_RESULT_CODE, resultCode);
        localBroadcastManager.sendBroadcast(localIntent);

        stopSelf();
    }

    private static class DownloadApk extends AsyncTask<String, Integer, Integer> {

        private WeakReference<UpdateService> updateServiceWeakReference;
        private File storeFile;

        public DownloadApk(UpdateService service) {
            updateServiceWeakReference = new WeakReference<>(service);
            this.storeFile = service.mUpdate.storeDir;
        }

        private void saveNewVersion(long fileLength) {
            UpdateService service = updateServiceWeakReference.get();
            if (service != null) {
                service.mUpdateSharedPreferences.setNewVersionName(service.mUpdate.versionName);
                service.mUpdateSharedPreferences.setFileLength(fileLength);
            }
        }

        private boolean checkDownload(long fileLength) {
            UpdateService service = updateServiceWeakReference.get();
            if (service != null) {
                if (service.mUpdate.versionName.equals(service.mUpdateSharedPreferences.getNewVersionName())
                        && service.mUpdate.storeDir.exists()
                        && service.mUpdate.storeDir.length() == fileLength) {
                    return false;
                }
            }
            return true;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            UpdateService service = updateServiceWeakReference.get();
            if (service != null) {
                service.start();
            }
        }

        @Override
        protected Integer doInBackground(String... params) {
            String downloadUrl = params[0];
            HttpURLConnection httpConnection = null;
            InputStream is = null;
            FileOutputStream fos = null;
            int updateTotalSize;
            try {
                if (DEBUG) {
                    Log.d(TAG, "download url is " + downloadUrl);
                    Log.d(TAG, "download apk cache at " + storeFile.getAbsolutePath());
                }
                File dir = storeFile.getParentFile();
                if (!dir.exists()) {
                    dir.mkdirs();
                }

                URL url = new URL(downloadUrl);
                httpConnection = (HttpURLConnection) url.openConnection();
                httpConnection.setConnectTimeout(20000);
                httpConnection.setReadTimeout(20000);

                if (DEBUG) {
                    Log.d(TAG, "download status code: " + httpConnection.getResponseCode());
                }

                if (httpConnection.getResponseCode() != 200) {
                    return UPDATE_RESULT_ERROR_SERVER;
                }

                updateTotalSize = httpConnection.getContentLength();


                if (checkDownload(updateTotalSize)) {
                    saveNewVersion(updateTotalSize);
                    if (storeFile.exists()) {
                        storeFile.delete();
                    }
                } else {
                    // 下载完成
                    return UPDATE_RESULT_SUCCESS;
                }

                //判别内存是否足够
                if (getFreeDiskSpace(dir) * 1024 < updateTotalSize) {
                    return UPDATE_RESULT_ERROR_SDCARD;
                }

                storeFile.createNewFile();
                is = httpConnection.getInputStream();
                fos = new FileOutputStream(storeFile, false);
                byte buffer[] = new byte[4096];

                int readSize = 0;
                int currentSize = 0;

                while ((readSize = is.read(buffer)) > 0) {
                    fos.write(buffer, 0, readSize);
                    currentSize += readSize;
                    publishProgress((currentSize * 100 / updateTotalSize));
                }
                // download success
            } catch (Exception e) {
                e.printStackTrace();
                if (e instanceof UnknownHostException
                        || e instanceof ConnectException
                        || e instanceof SocketException
                        || e instanceof SocketTimeoutException) {
                    return UPDATE_RESULT_ERROR_NETWORK;
                } else {
                    return UPDATE_RESULT_ERROR_OTHER;
                }
            } finally {
                if (httpConnection != null) {
                    httpConnection.disconnect();
                }
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return UPDATE_RESULT_SUCCESS;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            if (DEBUG) {
                Log.d(TAG, "current progress is " + values[0]);
            }
            UpdateService service = updateServiceWeakReference.get();
            if (service != null) {
                service.update(values[0]);
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            UpdateService service = updateServiceWeakReference.get();
            if (service != null) {
                if (result == UPDATE_RESULT_SUCCESS) {
                    service.success();
                } else {
                    service.error(result);
                }
            }
        }

        /**
         * 计算SD卡的剩余空间
         *
         * @return 返回-1，说明没有安装sd卡
         */
        public long getFreeDiskSpace(File path) {
            long freeSpace = 0;
            try {
                StatFs stat = new StatFs(path.getAbsolutePath());
                long blockSize = stat.getBlockSize();
                long availableBlocks = stat.getAvailableBlocks();
                freeSpace = availableBlocks * blockSize / 1024;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return freeSpace;
        }
    }
}
